<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>JavaScript设计模式示例</title>
  <style>
    body {
      font-family: 'Arial', sans-serif;
      line-height: 1.6;
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
      color: #333;
    }
    h1 {
      color: #2c3e50;
      border-bottom: 2px solid #eee;
      padding-bottom: 10px;
    }
    .pattern {
      background-color: #f8f9fa;
      border-left: 4px solid #42b983;
      padding: 15px;
      margin-bottom: 20px;
      border-radius: 0 4px 4px 0;
    }
    .output {
      background-color: #f1f1f1;
      padding: 10px;
      border-radius: 4px;
      margin-top: 10px;
      font-family: monospace;
      min-height: 20px;
    }
    button {
      background-color: #42b983;
      color: white;
      border: none;
      padding: 8px 15px;
      border-radius: 4px;
      cursor: pointer;
      margin-right: 5px;
      margin-bottom: 5px;
    }
    button:hover {
      background-color: #3aa876;
    }
    code {
      background-color: #f1f1f1;
      padding: 2px 5px;
      border-radius: 3px;
      font-family: monospace;
    }
    pre {
      background-color: #f1f1f1;
      padding: 10px;
      border-radius: 4px;
      overflow-x: auto;
    }
  </style>
</head>
<body>
  <h1>JavaScript设计模式示例</h1>
  
  <div class="pattern">
    <h2>单例模式 (Singleton Pattern)</h2>
    <p>确保一个类只有一个实例，并提供一个全局访问点。</p>
    <button onclick="demonstrateSingleton()">运行示例</button>
    <div id="singleton-output" class="output"></div>
  </div>
  
  <div class="pattern">
    <h2>工厂模式 (Factory Pattern)</h2>
    <p>创建对象的接口，让子类决定实例化哪个类。</p>
    <button onclick="demonstrateFactory()">运行示例</button>
    <div id="factory-output" class="output"></div>
  </div>
  
  <div class="pattern">
    <h2>观察者模式 (Observer Pattern)</h2>
    <p>定义对象间的一种一对多依赖关系，使得当一个对象状态改变时，所有依赖于它的对象都会得到通知并自动更新。</p>
    <button onclick="demonstrateObserver()">运行示例</button>
    <div id="observer-output" class="output"></div>
  </div>
  
  <div class="pattern">
    <h2>模块模式 (Module Pattern)</h2>
    <p>使用闭包创建私有变量和方法，只暴露公共接口。</p>
    <button onclick="demonstrateModule()">运行示例</button>
    <div id="module-output" class="output"></div>
  </div>
  
  <div class="pattern">
    <h2>装饰器模式 (Decorator Pattern)</h2>
    <p>动态地给对象添加额外的职责，比继承更灵活。</p>
    <button onclick="demonstrateDecorator()">运行示例</button>
    <div id="decorator-output" class="output"></div>
  </div>
  
  <div class="pattern">
    <h2>策略模式 (Strategy Pattern)</h2>
    <p>定义一系列算法，把它们封装起来，并且使它们可以互相替换。</p>
    <button onclick="demonstrateStrategy()">运行示例</button>
    <div id="strategy-output" class="output"></div>
  </div>
  
  <div class="pattern">
    <h2>命令模式 (Command Pattern)</h2>
    <p>将请求封装成对象，从而使你可以用不同的请求对客户进行参数化。</p>
    <button onclick="demonstrateCommand()">运行示例</button>
    <div id="command-output" class="output"></div>
  </div>

  <script>
    // 辅助函数：将内容添加到输出区域
    function appendToOutput(outputId, content) {
      const output = document.getElementById(outputId);
      output.innerHTML += content + '<br>';
    }
    
    // 辅助函数：清空输出区域
    function clearOutput(outputId) {
      const output = document.getElementById(outputId);
      output.innerHTML = '';
    }
    
    // 单例模式示例
    function demonstrateSingleton() {
      clearOutput('singleton-output');
      
      // 单例模式实现
      class Singleton {
        constructor() {
          if (Singleton.instance) {
            return Singleton.instance;
          }
          
          Singleton.instance = this;
          this.timestamp = new Date().toLocaleTimeString();
          this.randomNumber = Math.floor(Math.random() * 1000);
        }
        
        getData() {
          return `单例创建于: ${this.timestamp}, 随机数: ${this.randomNumber}`;
        }
      }
      
      // 测试单例
      appendToOutput('singleton-output', '创建第一个实例:');
      const instance1 = new Singleton();
      appendToOutput('singleton-output', instance1.getData());
      
      appendToOutput('singleton-output', '<br>创建第二个实例:');
      const instance2 = new Singleton();
      appendToOutput('singleton-output', instance2.getData());
      
      appendToOutput('singleton-output', '<br>两个实例是否相同: ' + (instance1 === instance2));
    }
    
    // 工厂模式示例
    function demonstrateFactory() {
      clearOutput('factory-output');
      
      // 工厂模式实现
      class UserFactory {
        createUser(type, userData) {
          switch(type) {
            case 'admin':
              return new AdminUser(userData);
            case 'regular':
              return new RegularUser(userData);
            case 'guest':
              return new GuestUser(userData);
            default:
              throw new Error(`不支持的用户类型: ${type}`);
          }
        }
      }
      
      class AdminUser {
        constructor(data) {
          this.name = data.name;
          this.permissions = ['read', 'write', 'delete', 'admin'];
        }
        
        describe() {
          return `管理员: ${this.name}, 权限: ${this.permissions.join(', ')}`;
        }
      }
      
      class RegularUser {
        constructor(data) {
          this.name = data.name;
          this.permissions = ['read', 'write'];
        }
        
        describe() {
          return `普通用户: ${this.name}, 权限: ${this.permissions.join(', ')}`;
        }
      }
      
      class GuestUser {
        constructor(data) {
          this.name = data.name || 'Guest';
          this.permissions = ['read'];
        }
        
        describe() {
          return `访客: ${this.name}, 权限: ${this.permissions.join(', ')}`;
        }
      }
      
      // 使用工厂
      const factory = new UserFactory();
      
      appendToOutput('factory-output', '创建不同类型的用户:');
      const admin = factory.createUser('admin', { name: '张三' });
      const regular = factory.createUser('regular', { name: '李四' });
      const guest = factory.createUser('guest', { name: '王五' });
      
      appendToOutput('factory-output', admin.describe());
      appendToOutput('factory-output', regular.describe());
      appendToOutput('factory-output', guest.describe());
    }
    
    // 观察者模式示例
    function demonstrateObserver() {
      clearOutput('observer-output');
      
      // 观察者模式实现
      class EventEmitter {
        constructor() {
          this.events = {};
        }
        
        // 订阅事件
        on(eventName, callback) {
          if (!this.events[eventName]) {
            this.events[eventName] = [];
          }
          this.events[eventName].push(callback);
          
          return () => this.off(eventName, callback);  // 返回取消订阅函数
        }
        
        // 取消订阅
        off(eventName, callback) {
          if (!this.events[eventName]) return;
          
          this.events[eventName] = this.events[eventName]
            .filter(cb => cb !== callback);
        }
        
        // 触发事件
        emit(eventName, ...args) {
          if (!this.events[eventName]) return;
          
          this.events[eventName].forEach(callback => {
            callback(...args);
          });
        }
        
        // 只订阅一次
        once(eventName, callback) {
          const onceCallback = (...args) => {
            callback(...args);
            this.off(eventName, onceCallback);
          };
          
          return this.on(eventName, onceCallback);
        }
      }
      
      // 使用观察者模式
      const emitter = new EventEmitter();
      
      appendToOutput('observer-output', '订阅事件:');
      
      // 订阅事件
      const unsubscribe = emitter.on('userLoggedIn', user => {
        appendToOutput('observer-output', `用户登录: ${user.name}`);
      });
      
      emitter.once('notification', message => {
        appendToOutput('observer-output', `通知(只触发一次): ${message}`);
      });
      
      // 触发事件
      appendToOutput('observer-output', '<br>触发事件:');
      emitter.emit('userLoggedIn', { name: '张三', id: 123 });
      emitter.emit('notification', '欢迎回来！');
      
      // 再次触发，once绑定的不会执行
      appendToOutput('observer-output', '<br>再次触发事件:');
      emitter.emit('notification', '这条通知不会显示');
      
      // 取消订阅
      appendToOutput('observer-output', '<br>取消订阅后:');
      unsubscribe();
      emitter.emit('userLoggedIn', { name: '李四', id: 456 });
    }
    
    // 模块模式示例
    function demonstrateModule() {
      clearOutput('module-output');
      
      // 模块模式实现
      const Counter = (function() {
        // 私有变量
        let count = 0;
        
        // 私有方法
        function validate(value) {
          return typeof value === 'number' && isFinite(value);
        }
        
        // 公共接口
        return {
          increment() {
            return ++count;
          },
          
          decrement() {
            return --count;
          },
          
          getValue() {
            return count;
          },
          
          setValue(value) {
            if (validate(value)) {
              count = value;
              return true;
            }
            return false;
          }
        };
      })();
      
      // 使用模块
      appendToOutput('module-output', `初始值: ${Counter.getValue()}`);
      
      appendToOutput('module-output', `递增: ${Counter.increment()}`);
      appendToOutput('module-output', `递增: ${Counter.increment()}`);
      
      appendToOutput('module-output', `设置值为10: ${Counter.setValue(10)}`);
      appendToOutput('module-output', `当前值: ${Counter.getValue()}`);
      
      appendToOutput('module-output', `递减: ${Counter.decrement()}`);
      
      // 尝试访问私有变量和方法
      appendToOutput('module-output', '<br>尝试访问私有成员:');
      appendToOutput('module-output', `Counter.count: ${Counter.count}`);
      appendToOutput('module-output', `Counter.validate: ${Counter.validate}`);
    }
    
    // 装饰器模式示例
    function demonstrateDecorator() {
      clearOutput('decorator-output');
      
      // 基础组件
      class Coffee {
        getCost() {
          return 15;
        }
        
        getDescription() {
          return '普通咖啡';
        }
      }
      
      // 装饰器基类
      class CoffeeDecorator {
        constructor(coffee) {
          this.coffee = coffee;
        }
        
        getCost() {
          return this.coffee.getCost();
        }
        
        getDescription() {
          return this.coffee.getDescription();
        }
      }
      
      // 具体装饰器
      class MilkDecorator extends CoffeeDecorator {
        getCost() {
          return this.coffee.getCost() + 5;
        }
        
        getDescription() {
          return this.coffee.getDescription() + ', 加奶';
        }
      }
      
      class SugarDecorator extends CoffeeDecorator {
        getCost() {
          return this.coffee.getCost() + 2;
        }
        
        getDescription() {
          return this.coffee.getDescription() + ', 加糖';
        }
      }
      
      class WhippedCreamDecorator extends CoffeeDecorator {
        getCost() {
          return this.coffee.getCost() + 8;
        }
        
        getDescription() {
          return this.coffee.getDescription() + ', 加奶油';
        }
      }
      
      // 使用装饰器模式
      let coffee = new Coffee();
      appendToOutput('decorator-output', `${coffee.getDescription()}: ¥${coffee.getCost()}`);
      
      // 加奶
      coffee = new MilkDecorator(coffee);
      appendToOutput('decorator-output', `${coffee.getDescription()}: ¥${coffee.getCost()}`);
      
      // 加糖
      coffee = new SugarDecorator(coffee);
      appendToOutput('decorator-output', `${coffee.getDescription()}: ¥${coffee.getCost()}`);
      
      // 加奶油
      coffee = new WhippedCreamDecorator(coffee);
      appendToOutput('decorator-output', `${coffee.getDescription()}: ¥${coffee.getCost()}`);
    }
    
    // 策略模式示例
    function demonstrateStrategy() {
      clearOutput('strategy-output');
      
      // 策略接口
      class DiscountStrategy {
        calculate(amount) {}
      }
      
      // 具体策略
      class NoDiscount extends DiscountStrategy {
        calculate(amount) {
          return amount;
        }
        
        getDescription() {
          return '无折扣';
        }
      }
      
      class PercentageDiscount extends DiscountStrategy {
        constructor(percentage) {
          super();
          this.percentage = percentage;
        }
        
        calculate(amount) {
          return amount * (1 - this.percentage / 100);
        }
        
        getDescription() {
          return `${this.percentage}%折扣`;
        }
      }
      
      class FixedDiscount extends DiscountStrategy {
        constructor(fixed) {
          super();
          this.fixed = fixed;
        }
        
        calculate(amount) {
          return amount > this.fixed ? amount - this.fixed : 0;
        }
        
        getDescription() {
          return `固定减免¥${this.fixed}`;
        }
      }
      
      // 上下文
      class ShoppingCart {
        constructor(discountStrategy) {
          this.items = [];
          this.discountStrategy = discountStrategy;
        }
        
        addItem(item) {
          this.items.push(item);
        }
        
        setDiscountStrategy(discountStrategy) {
          this.discountStrategy = discountStrategy;
        }
        
        getTotal() {
          const amount = this.items.reduce((total, item) => total + item.price, 0);
          return this.discountStrategy.calculate(amount);
        }
        
        getDiscountDescription() {
          return this.discountStrategy.getDescription();
        }
      }
      
      // 使用策略模式
      const cart = new ShoppingCart(new NoDiscount());
      
      // 添加商品
      cart.addItem({ name: '商品1', price: 100 });
      cart.addItem({ name: '商品2', price: 50 });
      cart.addItem({ name: '商品3', price: 150 });
      
      appendToOutput('strategy-output', '购物车商品总价: ¥300');
      appendToOutput('strategy-output', `应用${cart.getDiscountDescription()}: ¥${cart.getTotal()}`);
      
      // 更改折扣策略
      cart.setDiscountStrategy(new PercentageDiscount(20));
      appendToOutput('strategy-output', `应用${cart.getDiscountDescription()}: ¥${cart.getTotal()}`);
      
      cart.setDiscountStrategy(new FixedDiscount(50));
      appendToOutput('strategy-output', `应用${cart.getDiscountDescription()}: ¥${cart.getTotal()}`);
    }
    
    // 命令模式示例
    function demonstrateCommand() {
      clearOutput('command-output');
      
      // 接收者
      class Light {
        turnOn() {
          return '灯已打开';
        }
        
        turnOff() {
          return '灯已关闭';
        }
      }
      
      class Fan {
        turnOn() {
          return '风扇已打开';
        }
        
        turnOff() {
          return '风扇已关闭';
        }
      }
      
      // 命令接口
      class Command {
        execute() {}
        undo() {}
      }
      
      // 具体命令
      class LightOnCommand extends Command {
        constructor(light) {
          super();
          this.light = light;
        }
        
        execute() {
          return this.light.turnOn();
        }
        
        undo() {
          return this.light.turnOff();
        }
      }
      
      class LightOffCommand extends Command {
        constructor(light) {
          super();
          this.light = light;
        }
        
        execute() {
          return this.light.turnOff();
        }
        
        undo() {
          return this.light.turnOn();
        }
      }
      
      class FanOnCommand extends Command {
        constructor(fan) {
          super();
          this.fan = fan;
        }
        
        execute() {
          return this.fan.turnOn();
        }
        
        undo() {
          return this.fan.turnOff();
        }
      }
      
      class FanOffCommand extends Command {
        constructor(fan) {
          super();
          this.fan = fan;
        }
        
        execute() {
          return this.fan.turnOff();
        }
        
        undo() {
          return this.fan.turnOn();
        }
      }
      
      // 调用者
      class RemoteControl {
        constructor() {
          this.commands = {};
          this.history = [];
        }
        
        setCommand(slot, command) {
          this.commands[slot] = command;
        }
        
        pressButton(slot) {
          const command = this.commands[slot];
          if (command) {
            const result = command.execute();
            this.history.push(command);
            return result;
          }
          return '没有分配命令';
        }
        
        pressUndoButton() {
          const command = this.history.pop();
          if (command) {
            return command.undo();
          }
          return '没有可撤销的命令';
        }
      }
      
      // 使用命令模式
      const light = new Light();
      const fan = new Fan();
      
      const lightOn = new LightOnCommand(light);
      const lightOff = new LightOffCommand(light);
      const fanOn = new FanOnCommand(fan);
      const fanOff = new FanOffCommand(fan);
      
      const remote = new RemoteControl();
      remote.setCommand('lightOn', lightOn);
      remote.setCommand('lightOff', lightOff);
      remote.setCommand('fanOn', fanOn);
      remote.setCommand('fanOff', fanOff);
      
      appendToOutput('command-output', '执行命令:');
      appendToOutput('command-output', remote.pressButton('lightOn'));
      appendToOutput('command-output', remote.pressButton('fanOn'));
      appendToOutput('command-output', remote.pressButton('lightOff'));
      
      appendToOutput('command-output', '<br>撤销命令:');
      appendToOutput('command-output', remote.pressUndoButton()); // 撤销 lightOff
      appendToOutput('command-output', remote.pressUndoButton()); // 撤销 fanOn
      appendToOutput('command-output', remote.pressUndoButton()); // 撤销 lightOn
      appendToOutput('command-output', remote.pressUndoButton()); // 没有可撤销的命令
    }
  </script>
</body>
</html>